/**
 * This is the main file where all the game mechanics happens so far
 */

#include <stdio.h> // Needed only for sprintf()
#include <math.h>  // Needed for sin(), cos() and M_PI
#include "GL/glfw.h" // Takes care of everything GL-related
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>

#include "include-win32/windowsFunctions.h"
#include "include/gameStructs.h"
#include "include/prototypes.h"
#include "include/viewports.h" 
#include "include/shaders.h"
#include "include/settings.h"

float fps = 0.0;

#include "include/playerManagementCollision.h"
#include "include/playerManagementMovement.h"
#include "include/playerManagement.h"
#include "include/objectManagement.h"
#include "include/objectManagementPopulation.h"
#include "include/objectManagementCollision.h"
#include "include/objectManagementAnimation.h"
#include "include/objectManagementWeapons.h"
#include "include/renderText.h"
#include "include/renderObjectsEffects.h"
#include "include/renderObjects.h"
#include "include/menuManagement.h"


/* Some global variables for convenience */
double t0 = 0.0;
int frames = 0;
char titlestring[200];



/*=========================================================================================================
 ===========================================================================================================
 this part should be completely rewritten 
 from here
 ===========================================================================================================
 ==========================================================================================================*/
/**
 * Calculates the FPS
 */
void showFPS() {
    double t;
    t = glfwGetTime();
    if( (t-t0) > 1.0 || frames == 0 )
    {
        fps = (double)frames / (t-t0);
        sprintf(titlestring, "Render (%.1f FPS) OpenGL: %s", fps,glGetString(GL_VERSION));
        glfwSetWindowTitle(titlestring);
        t0 = t;
        frames = 0;
    }
    frames ++;
}

/*=========================================================================================================
 ===========================================================================================================
 to here
 ===========================================================================================================
 ==========================================================================================================*/


/**
 *	main(argc, argv) - the standard C entry point for the program
 */
int main(int argc, char *argv[]) {
	/*========================================================================
	 Settings
	 =========================================================================*/
	loadSettings(OS_SETTINGS_PATH(bowlwars_config.cfg));
	int windowWidth = atoi(settingsArray[SETTINGS_WINDOW_WIDTH]);
	int windowHeight = atoi(settingsArray[SETTINGS_WINDOW_HEIGHT]);
	int fullscreen = atoi(settingsArray[SETTINGS_WINDOW_FULLSCREEN]);

	/*========================================================================
	 initialize window
	 =========================================================================*/
	glfwInit();
	glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); // activate anti-alias 4x
	if (fullscreen) {
		if( !glfwOpenWindow(windowWidth, windowHeight, 8,8,8,8, 32,0, GLFW_FULLSCREEN) )
		{
			printf("Window could not be opened, terminating!\n\nTry disabling anti-alias, maybe?");
			glfwTerminate(); // glfwOpenWindow failed, quit the program.
			return 1;
		}
	} else {
		if( !glfwOpenWindow(windowWidth, windowHeight, 8,8,8,8, 32,0, GLFW_WINDOW) )
		{
			printf("Window could not be opened, terminating!\n\nTry disabling anti-alias, maybe?");
			glfwTerminate(); // glfwOpenWindow failed, quit the program.
			return 1;
		}
	}
	printf("\n\nWindow opened: %ix%i\n\n",windowWidth,windowHeight);
	
	// initialize some GL variables
	initializeGL();
	
	
	
	/*========================================================================
	 Initializing players and all static objects in the world
	 =========================================================================*/
	PlayerObject player1, player2;
	playerInitialization(&player1,&player2);
	modelArrayPopulation();
	//objectPopulation();
	
	/*========================================================================
	 Initializing fonts and defining color and position for the on-screen stats
	 =========================================================================*/
	textMessageAdd("Game started!");
	initTextFonts();
	Color theColor; theColor.r = 1.0f; theColor.g = 0.0f; theColor.b = 0.0f;
	int player1Textypos = windowHeight / 2 - 70;
	int player2Textypos = windowHeight - 70;
	
    /*========================================================================
	 Skybox
	 =========================================================================*/
	 StaticObject skybox;
	 createSkybox(&skybox);
	
	/*========================================================================
	 Initializing constants so they dont have to be redeclared in the game loop
	 =========================================================================*/
	int loop=0;
	int objCount=0;
	int gameMode = GL_FALSE;
	
	// light source 
	float vector4f_center[4]={0.0f, 0.0f, 20.0f, 2.0f}; // Big Sun in origo	
	
	// Create light components
	GLfloat extra_ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
	GLfloat extra_diffuseLight[] = { 0.8f, 0.8f, 0.8, 1.0f };
	GLfloat extra_specularLight[] = { 0.5f, 0.5f, 0.5f, 1.0f };
	GLfloat extra_position[] = {80.0f, 80.0f, 20.0f,1.0f};
	
	
	GLfloat ambient[] = { 0.2f, 0.2f, 0.2f };
	
	// Fog
	GLfloat fogColor[4]= {0.3804f, 0.7843f, 0.8196f, 1.0f};
	glFogi(GL_FOG_MODE, GL_LINEAR);		// Fog Mode
    glFogfv(GL_FOG_COLOR, fogColor);			// Set Fog Color
    glFogf(GL_FOG_DENSITY, 0.35f);				// How Dense Will The Fog Be
    glHint(GL_FOG_HINT, GL_DONT_CARE);			// Fog Hint Value
    glFogf(GL_FOG_START, 70.0f);				// Fog Start Depth
    glFogf(GL_FOG_END, 170.0f);				// Fog End Depth
    //glEnable(GL_FOG);					// Enables GL_FOG
    
	
	// MENU CONSTANTS
	double mt0 = 0.0;
	double mt;	
	int activeMenuItem = 1;	
	gamePlaying = GL_FALSE;
	
	// Load menu images
	int textures[] = {0,0};
	glGenTextures(2, textures); 
	glBindTexture(GL_TEXTURE_2D, textures[0]);
	glfwLoadTexture2D( OS_IMG_PATH(menu_default.tga), GLFW_BUILD_MIPMAPS_BIT );
	// Use trilinear interpolation for minification
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
					GL_LINEAR_MIPMAP_LINEAR );
	
	// Use bilinear interpolation for magnification
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
					GL_LINEAR );
	glBindTexture(GL_TEXTURE_2D,0);
	
	gameReset(&player1,&player2);
	
    /*========================================================================
	 Main loop
	 =========================================================================*/
    int programRunning = GL_TRUE; // Main loop exits when this is set to GL_FALSE
    while(programRunning)
    {
		
		srand(time(0));
		showFPS();
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
		
		if (gameMode) {
			
			/*========================================================================
			 View game menu
			 =========================================================================*/
			if (glfwGetKey(GLFW_KEY_ESC)) {
				gameMode = GL_FALSE;
				activeMenuItem = 0;
			}

			/*========================================================================
			 Do the player movements (controls)
			 =========================================================================*/
			controlPlayer(&player1, 'W','S','A','D','E');
			controlPlayer(&player2, GLFW_KEY_UP,GLFW_KEY_DOWN,GLFW_KEY_LEFT,GLFW_KEY_RIGHT,GLFW_KEY_ENTER);
		}

		
		if (fps > 0.0f) {
			/*========================================================================
			 Check for player collisions
			 =========================================================================*/
			// maybe rename and check if the collide on each other and calculate angle and so on and so on
			//boundingSphere(&player1, &player2);
			
			/*========================================================================
			 Check for bullet collisions
			 =========================================================================*/
			int i = 0;
			while (i<BULLET_ARRAY_MAX) {
				if (bulletArray[i] != 0) {
					int remove = 0;
					remove += moveBullet(bulletArray[i]);
					remove += checkBulletCollision(&player1, &player2, bulletArray[i]);
					objCount=0;
					while (objCount<COLLISIONOBJECT_ARRAY_MAX && collisionObjectArray[objCount] != 0) {
						CollisionObject *obj = collisionObjectArray[objCount];
						if (obj->collideType != OBJECT_DISABLED) {
							remove += checkBulletCollisionObject(bulletArray[i],obj);
						}
						objCount++;
					}
					if (remove>0) {
						removeBullet(bulletArray[i]);
					}
				}
				i++;
			}
			
			/*========================================================================
			 Check if players collides with objects
			 =========================================================================*/
			objCount=0;
			while (objCount<COLLISIONOBJECT_ARRAY_MAX && collisionObjectArray[objCount] != 0) {
				CollisionObject *obj = collisionObjectArray[objCount];
				
				moveCollisionObject(obj);
				
				if (obj->collideType != OBJECT_DISABLED) {
					
					bumpCheck(&player1, obj);
					bumpCheck(&player2, obj);
					
					// check all other objects
					int objCountSecondPass = objCount+1;
					while (objCountSecondPass < COLLISIONOBJECT_ARRAY_MAX && collisionObjectArray[objCountSecondPass] != 0) {
						CollisionObject *obj2 = collisionObjectArray[objCountSecondPass];
						bumpCheckObjects(obj,obj2);
						objCountSecondPass++;
					}
				}
				objCount++;
			}
		}		

		/*========================================================================
		 Loop to update 2 viewports (split-screen)
		 =========================================================================*/
		loop=0;
		while(loop<2){
            //Disable fog 
            glDisable(GL_FOG);
			if (!gameMode) {	
				loop = 3;
			}
			
								

			if(loop == 0){
				setupCameraPlayer1();				
				followCamera(&player1, &skybox);
				glEnable(GL_FOG);							
			}
			if(loop == 1){
				setupCameraPlayer2();			
				followCamera(&player2, &skybox);
				glEnable(GL_FOG);				
			}
			if(loop == 3){
				setupMenuCamera();				
				menuCamera(&skybox);           
				glEnable(GL_FOG);
			}			
			
								
			
			/*========================================================================
			 Enable Light
			 =========================================================================*/				

			glLightfv(GL_LIGHT0, GL_POSITION, vector4f_center); // Set light position			
			glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
			
			glLightfv(GL_LIGHT1, GL_AMBIENT, extra_ambientLight);
			glLightfv(GL_LIGHT1, GL_DIFFUSE, extra_diffuseLight);
			glLightfv(GL_LIGHT1, GL_SPECULAR, extra_specularLight);
			glLightfv(GL_LIGHT1, GL_POSITION, extra_position);
			
			glEnable(GL_LIGHT0);
			glEnable(GL_LIGHT1);				
			glEnable(GL_LIGHTING);
			
			/*========================================================================
			 Do the player drawing
			 =========================================================================*/
			drawPlayer(&player1);
			drawPlayer(&player2);
			
			
			/*========================================================================
			 Draw the collidable objects
			 =========================================================================*/
			objCount=0;
			while (objCount<COLLISIONOBJECT_ARRAY_MAX && collisionObjectArray[objCount] != 0) {
				CollisionObject *obj = collisionObjectArray[objCount];
				if (obj->visible && obj->collideType != OBJECT_DISABLED && obj->shader == 0) {
					drawCollisionObject(obj);
				}
				if (obj->shader != 0) {
					renderCollisionObjectShader(obj);
				}
				objCount++;
			}
			
			/*========================================================================
			 Draw the bullets
			 =========================================================================*/
			int i = 0;
			while (i<BULLET_ARRAY_MAX) {
				if (bulletArray[i] != 0) {
					drawBullet(bulletArray[i]);
				}
				i++;
			}
			
			/*========================================================================
			 Disable the light source
			 =========================================================================*/
			glDisable(GL_LIGHTING);
			glDisable(GL_LIGHT0);	
			glDisable(GL_LIGHT1);		
			
			// prepare for next rendering
			loop++;
		}
		
			
			
		/*========================================================================
		 Print user stats, maybe put in abstract function on viewport overlay
		 =========================================================================*/
		setupTextOverlay();
		
		
		if (gameMode) {			
			printStats(&player1,&player2);
		} else {
		
			/*========================================================================
			 Game in Menu Mode
			 =========================================================================*/
			
			// Create menu items
			const int menuItemsCount = 6;
			char *menuItems[menuItemsCount];

			int mi;
			for (mi=0; mi<menuItemsCount; mi++) {
				menuItems[mi] = (char*)malloc(sizeof(char)*30);	
			}
			
			strcpy(menuItems[0],"RESUME");
			strcpy(menuItems[1],"PLAY");
			strcpy(menuItems[2],"ABOUT");
			strcpy(menuItems[3],"CONTROLS");
			strcpy(menuItems[4],"SETTINGS");
			strcpy(menuItems[5],"EXIT GAME");					

			/*========================================================================
			 Menu interactions
			 =========================================================================*/

			renderMenuBackground();
			renderMenu(activeMenuItem, menuItemsCount, menuItems);	
			renderMenuItemContent(activeMenuItem);				
			
			
			// Move interactions to function menuInteractions() in menuManagement.h
			
			if (glfwGetKey(GLFW_KEY_DOWN)) {
				mt = glfwGetTime();
				double temp = (mt-t0);
				
				if ((mt-t0) > 0.2) {
					if (activeMenuItem <(menuItemsCount-1)) {
						activeMenuItem++;
					} else {
						if (gamePlaying == GL_TRUE) {
							activeMenuItem = 0;
						} else {
							activeMenuItem = 1;
						}

					}
					t0 = mt;
				}	
			}
			if (glfwGetKey(GLFW_KEY_UP)) {
				mt = glfwGetTime();
				double temp = (mt-t0);
				if ((mt-t0) > 0.2) {
					if (activeMenuItem > 1) {
						activeMenuItem--;
					} else {
						if (gamePlaying == GL_TRUE) {
							if (activeMenuItem == 1) {
								activeMenuItem = 0;
							} else {
								activeMenuItem = (menuItemsCount-1);
							}

						} else {
							activeMenuItem = (menuItemsCount-1);
						}
						
					}
					t0 = mt;
				} 
			}			
			
			// Actions when menu item is chosen with Enter key.
			if (glfwGetKey(GLFW_KEY_ENTER)) {
				switch (activeMenuItem) {
					case 0: // PLAY
						gameMode = GL_TRUE;
						break;
					case 1: // NEW GAME
						gameMode = GL_TRUE;
						gamePlaying = GL_TRUE;
						gameReset(&player1,&player2);
						break;
					case 2: // ABOUT
						break;
					case 3: // CONTROLS
						break;
					case 4: // SETTINGS
						break;						
					case 5: // EXIT
						printf("Goodbye!\n");
						programRunning = GL_FALSE;
						break;
					default:
						break;
				}
			}
			
			if(glfwGetKey('Q') || !glfwGetWindowParam(GLFW_OPENED))
				programRunning = GL_FALSE;

        }

		/*========================================================================
		 actually push changes to the screen, close if escape key is pressed
		 =========================================================================*/
		glfwSwapBuffers();
		
    }
	
    // Close the OpenGL window and terminate GLFW.
    glfwTerminate();
	ExitFunction();
	printf("\n\nBowlWars has ended successfully!\n\n");
	
    return 0;
}
